前两章说了crash文件解析的方法和crash文件中包含的内容,基于的条件是拿到当前奔溃的手机,再拿到手机中对应的日志。或者,用户打开了日志上报的功能,统计到的日志被apple收集了。这两种方式都比较被动,而且没有筛选的功能。对于监测app状态来说不是很准确,定位的过程也比较长。
自定义一个crash 收集工具
上面已经介绍了背景,接下来开始自定义工具,先画一个逻辑图来阐述工具具备的功能
一步一步来,首先在客户端收集crash,收集的方式包含两种:
- NSSetUncaughtExceptionHandler
- handleSignal
至于hook Unix信号还是 mac内核信号,这个地址中给出了详细的介绍,这里就不在赘述了。工程中的函数如下:
需要include对应的头文件
#include <signal.h>
#include <execinfo.h>
- (void)crashHandler {
signal(SIGHUP, signalHandler);
signal(SIGINT, signalHandler);
signal(SIGQUIT, signalHandler);
signal(SIGABRT, signalHandler);
signal(SIGILL, signalHandler);
signal(SIGSEGV, signalHandler);
signal(SIGFPE, signalHandler);
signal(SIGBUS, signalHandler);
signal(SIGPIPE, signalHandler);
//异常时调用的函数
NSSetUncaughtExceptionHandler(&handleExceptions);
}
void handleExceptions(NSException *exception) {
NSLog(@"exception = %@",exception);
NSString *callStackString = [[exception callStackSymbols] componentsJoinedByString:@"\n"];
NSLog(@"callStackSymbols = %@",callStackString);
NSLog(@"path: %@", realPath);
// 当前将这个数据做文件保存
NSDictionary *dict = @{
@"name": exception.name ? : @"unknown",
@"reson": exception.reason ? : @"unknown",
@"userInfo": exception.userInfo ? : @"unknown",
@"callStackReturnAddresses": exception.callStackReturnAddresses ? : @"unknown",
@"callStackSymbols": callStackString ? : @"unknown"
};
NSLog(@"dict: %@",dict);
// 将文件写入对应的地址
NSLog(@"%@", realPath);
if ([dict writeToFile:realPath atomically:YES]) {
NSLog(@"写入数据成功");
}
for (NSInteger i = 0; i < 100; i ++) {
NSLog(@"index : %ld", (long)i);
}
}
NSString *signalMessageBy(int sig) {
switch (sig) {
case SIGHUP:
return @"SIGHUP (hangup)";
break;
case SIGINT:
return @"SIGINT (interrupt)";
break;
case SIGQUIT:
return @"SIGQUIT (quit)";
break;
case SIGABRT:
return @"SIGABRT (abort())";
break;
case SIGILL:
return @"SIGILL (illegal instruction (not reset when caught))";
break;
case SIGSEGV:
return @"SIGSEGV (segmentation violation)";
break;
case SIGFPE:
return @"SIGFPE (floating point exception)";
break;
case SIGBUS:
return @"SIGBUS (bus error)";
break;
case SIGPIPE:
return @"SIGPIPE (write on a pipe with no one to read it)";
break;
default:
return @"unknown";
break;
}
}
void signalHandler(int sig) {
NSMutableString *mstr = [[NSMutableString alloc] init];
[mstr appendString:@"Stack:\n"];
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i <frames; ++i) {
[mstr appendFormat:@"%s\n", strs[i]];
}
// 这里在测试的时候是不会断点到的,所以需要存储在文件中
// 当前将这个数据做文件保存
NSDictionary *dict = @{
@"name": @"UncaughtExceptionHandlerSignalExceptionName",
@"reson": [NSString stringWithFormat:@"Signal %@ was raised.", signalMessageBy(sig)],
@"userInfo": @{ @"UncaughtExceptionHandlerSignalKey" : @(sig) },
@"callStackReturnAddresses": @"unknown",
@"callStackSymbols": mstr ? : @"unknown"
};
NSLog(@"dict: %@",dict);
// 将文件写入对应的地址
NSLog(@"%@", signCrashPath);
if ([dict writeToFile:signCrashPath atomically:YES]) {
NSLog(@"写入数据成功");
}
}
根据不同的奔溃类型,手动写了两处奔溃的代码:
// 数组插入空值
- (void)function9 {
NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];
}
// 访问野指针
struct Test *test = {1,2};
free(test);
test->a;
// struct 定义
struct Test {
int a;
int b;
};
代码介绍完了,采用release的方式build到真机,运行crash,再从xcode device中下载当前真机中的内容,查看当前文件中的数据:数据如下:
exception存储的文件信息
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>callStackReturnAddresses</key> <array> <integer>6493351308</integer> <integer>6478382572</integer> <integer>6492927824</integer> <integer>6492090460</integer> <integer>4333932636</integer> <integer>6658430540</integer> <integer>6659614832</integer> <integer>6658455296</integer> <integer>6659723688</integer> <integer>6658968032</integer> <integer>6658922640</integer> <integer>6658916816</integer> <integer>6667181340</integer> <integer>6667190984</integer> <integer>6667162472</integer> <integer>6492992516</integer> <integer>6492990508</integer> <integer>6492981148</integer> <integer>6492065192</integer> <integer>6525509664</integer> <integer>6659966808</integer> <integer>4333932852</integer> <integer>6486368192</integer> </array> <key>callStackSymbols</key> <string>0 CoreFoundation 0x000000018308ada4 <redacted> + 252 1 libobjc.A.dylib 0x00000001822445ec objc_exception_throw + 56 2 CoreFoundation 0x0000000183023750 _CFArgv + 0 3 CoreFoundation 0x0000000182f5705c <redacted> + 1412 4 TestCrash 0x000000010252905c -[ViewController function9] + 60 5 UIKit 0x000000018cdf964c <redacted> + 96 6 UIKit 0x000000018cf1a870 <redacted> + 80 7 UIKit 0x000000018cdff700 <redacted> + 440 8 UIKit 0x000000018cf351a8 <redacted> + 572 9 UIKit 0x000000018ce7c9e0 <redacted> + 2428 10 UIKit 0x000000018ce71890 <redacted> + 3160 11 UIKit 0x000000018ce701d0 <redacted> + 340 12 UIKit 0x000000018d651d1c <redacted> + 2340 13 UIKit 0x000000018d6542c8 <redacted> + 4744 14 UIKit 0x000000018d64d368 <redacted> + 152 15 CoreFoundation 0x0000000183033404 <redacted> + 24 16 CoreFoundation 0x0000000183032c2c <redacted> + 276 17 CoreFoundation 0x000000018303079c <redacted> + 1204 18 CoreFoundation 0x0000000182f50da8 CFRunLoopRunSpecific + 552 19 GraphicsServices 0x0000000184f36020 GSEventRunModal + 100 20 UIKit 0x000000018cf70758 UIApplicationMain + 236 21 TestCrash 0x0000000102529134 main + 88 22 libdyld.dylib 0x00000001829e1fc0 <redacted> + 4</string> <key>name</key> <string>NSInvalidArgumentException</string> <key>reson</key> <string>*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil</string> <key>userInfo</key> <string>unknown</string> </dict> </plist>
signal 存储的文件信息
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>callStackReturnAddresses</key> <string>unknown</string> <key>callStackSymbols</key> <string>Stack: 0 TestCrash 0x00000001042f9264 signalHandler + 116 1 libsystem_platform.dylib 0x0000000182cacb48 _sigtramp + 36 2 libsystem_pthread.dylib 0x0000000182cb66a8 <redacted> + 360 3 libsystem_c.dylib 0x0000000182a7fd0c abort + 140 4 libsystem_malloc.dylib 0x0000000182b49838 <redacted> + 0 5 UIKit 0x000000018cdf964c <redacted> + 96 6 UIKit 0x000000018cf1a870 <redacted> + 80 7 UIKit 0x000000018cdff700 <redacted> + 440 8 UIKit 0x000000018cf351a8 <redacted> + 572 9 UIKit 0x000000018ce7c9e0 <redacted> + 2428 10 UIKit 0x000000018ce71890 <redacted> + 3160 11 UIKit 0x000000018ce701d0 <redacted> + 340 12 UIKit 0x000000018d651d1c <redacted> + 2340 13 UIKit 0x000000018d6542c8 <redacted> + 4744 14 UIKit 0x000000018d64d368 <redacted> + 152 15 CoreFoundation 0x0000000183033404 <redacted> + 24 16 CoreFoundation 0x0000000183032c2c <redacted> + 276 17 CoreFoundation 0x000000018303079c <redacted> + 1204 18 CoreFoundation 0x0000000182f50da8 CFRunLoopRunSpecific + 552 19 GraphicsServices 0x0000000184f36020 GSEventRunModal + 100 20 UIKit 0x000000018cf70758 UIApplicationMain + 236 21 TestCrash 0x00000001042f9134 main + 88 22 libdyld.dylib 0x00000001829e1fc0 <redacted> + 4 </string> <key>name</key> <string>UncaughtExceptionHandlerSignalExceptionName</string> <key>reson</key> <string>Signal SIGABRT (abort()) was raised.</string> <key>userInfo</key> <dict> <key>UncaughtExceptionHandlerSignalKey</key> <integer>6</integer> </dict> </dict> </plist>
这里先介绍一个符号化的原理,apple提供了一个atos
命令行工具,来完成符号化的过程。在crash的堆栈中,我们可以看到这样的信息
针对一行一行的堆栈信息做如下操作:
atos -arch <Binary Architecture> -o <Path to dSYM file>/Contents/Resources/DWARF/<binary image name> -l <load address> <address to symbolicate>
这里存在几个参数:
- 当前二进制的架构类型
- dSYM文件
- 二进制镜像加载地址
- 堆栈报错的地址
- 当前进程的UUID信息,用于匹配dSYM文件
如果上面的信息都有了,运行指令,例如apple给出的例子:
$ atos -arch arm64 -o TheElements.app.dSYM/Contents/Resources/DWARF/TheElements -l 0x1000e4000 0x00000001000effdc
-[AtomicElementViewController myTransitionDidStop:finished:context:]
问题来了,自定义收集到的信息中,缺少如下信息:
- 当前二进制的架构类型
- 二进制镜像加载的地址
- 当前进程的UUID信息
这些信息在哪里能够获取到呢?这里需要用到Mach-o相关的知识,二进制最终在手机上的呈现是以page的方式。在Mach-o层看到的信息为
- header
- load commad
- data
header部分就是协议部分,定义了当前二进制使用的框架类型等,在Header中可以拿到的信息有
- 二进制的框架类型
- 二进制中各个镜像的加载地址
- uuid
使用的方法为
// 这里存储一张表
NSMutableArray *globalProgressInfo;
void getProgressBinaryImagesInfo()
{
printf("Binary Images:\n");
//Get count of all currently loaded DYLD
uint32_t count = _dyld_image_count();
if (count > 0) {
globalProgressInfo = [NSMutableArray new];
for(uint32_t i = 0; i < count; i++)
{
NSMutableDictionary *dict = [NSMutableDictionary new];
//Name of image (includes full path)
const char *dyld = _dyld_get_image_name(i);
if (dyld && strlen(dyld) > 0) {
//Get name of file
int slength = strlen(dyld);
int j;
for(j = slength - 1; j>= 0; --j)
if(dyld[j] == '/') break;
//strndup only available in iOS 4.3
char *name = strndup(dyld + ++j, slength - j);
dict[@"name"] = [NSString stringWithFormat:@"%s", name];
free(name);
}
const struct mach_header *header = _dyld_get_image_header(i);
if (header) {
// image address
dict[@"imageAddress"] = [NSString stringWithFormat:@"0x%lX",(uintptr_t)header];
// arch
const NXArchInfo *info = NXGetArchInfoFromCpuType(header->cputype, header->cpusubtype);
if (info && info->name) {
dict[@"arch"] = [NSString stringWithFormat:@"%s", info->name];
}
// uuid
BOOL is64bit = header->magic == MH_MAGIC_64 || header->magic == MH_CIGAM_64;
uintptr_t cursor = (uintptr_t)header + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
const struct segment_command *segmentCommand = NULL;
for (uint32_t i = 0; i < header->ncmds; i++, cursor += segmentCommand->cmdsize)
{
segmentCommand = (struct segment_command *)cursor;
if (segmentCommand->cmd == LC_UUID)
{
const struct uuid_command *uuidCommand = (const struct uuid_command *)segmentCommand;
if (uuidCommand) {
dict[@"uuid"] = [[NSUUID alloc] initWithUUIDBytes:uuidCommand->uuid].UUIDString;
}
}
}
}
[globalProgressInfo addObject:dict];
}
}
}
输出的结果太多了:小一百个之多,所以在crash的时候是需要过滤的
(
{
arch = arm64;
imageAddress = 0x102544000;
name = TestCrash;
uuid = "812B393C-BAE2-3E1B-A14E-8593657A8935";
},
{
arch = arm64;
imageAddress = 0x102560000;
name = "libBacktraceRecording.dylib";
uuid = "CC6A753E-76FF-324E-8314-B0BAD859C354";
},
{
arch = arm64;
imageAddress = 0x102584000;
name = "libMainThreadChecker.dylib";
uuid = "3E93EDAA-8307-3BEA-8226-AEF09B0D193E";
},
......
)
经过排重之后上传的crash文件的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>callStackReturnAddresses</key>
<array>
<integer>6493351308</integer>
<integer>6478382572</integer>
<integer>6492927824</integer>
<integer>6492090460</integer>
<integer>4300867372</integer>
<integer>6658430540</integer>
<integer>6659614832</integer>
<integer>6658455296</integer>
<integer>6659723688</integer>
<integer>6658968032</integer>
<integer>6658922640</integer>
<integer>6658916816</integer>
<integer>6667181340</integer>
<integer>6667190984</integer>
<integer>6667162472</integer>
<integer>6492992516</integer>
<integer>6492990508</integer>
<integer>6492981148</integer>
<integer>6492065192</integer>
<integer>6525509664</integer>
<integer>6659966808</integer>
<integer>4300867588</integer>
<integer>6486368192</integer>
</array>
<key>callStackSymbols</key>
<string>0 CoreFoundation 0x000000018308ada4 <redacted> + 252
1 libobjc.A.dylib 0x00000001822445ec objc_exception_throw + 56
2 CoreFoundation 0x0000000183023750 _CFArgv + 0
3 CoreFoundation 0x0000000182f5705c <redacted> + 1412
4 TestCrash 0x00000001005a072c TestCrash + 34604
5 UIKit 0x000000018cdf964c <redacted> + 96
6 UIKit 0x000000018cf1a870 <redacted> + 80
7 UIKit 0x000000018cdff700 <redacted> + 440
8 UIKit 0x000000018cf351a8 <redacted> + 572
9 UIKit 0x000000018ce7c9e0 <redacted> + 2428
10 UIKit 0x000000018ce71890 <redacted> + 3160
11 UIKit 0x000000018ce701d0 <redacted> + 340
12 UIKit 0x000000018d651d1c <redacted> + 2340
13 UIKit 0x000000018d6542c8 <redacted> + 4744
14 UIKit 0x000000018d64d368 <redacted> + 152
15 CoreFoundation 0x0000000183033404 <redacted> + 24
16 CoreFoundation 0x0000000183032c2c <redacted> + 276
17 CoreFoundation 0x000000018303079c <redacted> + 1204
18 CoreFoundation 0x0000000182f50da8 CFRunLoopRunSpecific + 552
19 GraphicsServices 0x0000000184f36020 GSEventRunModal + 100
20 UIKit 0x000000018cf70758 UIApplicationMain + 236
21 TestCrash 0x00000001005a0804 TestCrash + 34820
22 libdyld.dylib 0x00000001829e1fc0 <redacted> + 4</string>
<key>imageInfo</key>
<dict>
<key>CoreFoundation</key>
<dict>
<key>arch</key>
<string>arm64</string>
<key>imageAddress</key>
<string>0x182F45000</string>
<key>name</key>
<string>CoreFoundation</string>
<key>uuid</key>
<string>533C841E-D6E9-313D-8ADB-02388744E2EF</string>
</dict>
<key>GraphicsServices</key>
<dict>
<key>arch</key>
<string>arm64</string>
<key>imageAddress</key>
<string>0x184F2B000</string>
<key>name</key>
<string>GraphicsServices</string>
<key>uuid</key>
<string>5011EC25-11D7-3A56-AF50-1E8207D54962</string>
</dict>
<key>TestCrash</key>
<dict>
<key>arch</key>
<string>arm64</string>
<key>imageAddress</key>
<string>0x100598000</string>
<key>name</key>
<string>TestCrash</string>
<key>uuid</key>
<string>8C66D9F9-9023-3E5C-B4E6-9BF8772ED730</string>
</dict>
<key>UIKit</key>
<dict>
<key>arch</key>
<string>arm64</string>
<key>imageAddress</key>
<string>0x18CC53000</string>
<key>name</key>
<string>UIKit</string>
<key>uuid</key>
<string>BE6EF020-3CAA-3939-86DA-6DD6737541D5</string>
</dict>
<key>libdyld.dylib</key>
<dict>
<key>arch</key>
<string>arm64</string>
<key>imageAddress</key>
<string>0x1829E1000</string>
<key>name</key>
<string>libdyld.dylib</string>
<key>uuid</key>
<string>6225B1CD-3984-3071-A64A-DD8F31B09C36</string>
</dict>
<key>libobjc.A.dylib</key>
<dict>
<key>arch</key>
<string>arm64</string>
<key>imageAddress</key>
<string>0x18223C000</string>
<key>name</key>
<string>libobjc.A.dylib</string>
<key>uuid</key>
<string>EB1135B2-BDE9-3B69-B96E-42CA98200183</string>
</dict>
</dict>
<key>name</key>
<string>NSInvalidArgumentException</string>
<key>reson</key>
<string>*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil</string>
<key>userInfo</key>
<string>unknown</string>
</dict>
</plist>
对于解析堆栈来说,这里的数据就已经足够了。
题外话
到目前为止,还没有介绍dSYM是什么,长什么样子。dSYM文件通过nm
指令可以查看符号文件中的内容。例如:
nm TestCrash.app.dSYM/Contents/Resources/DWARF/TestCrash > a.txt
产出的文件如下:
0000000100007034 t +[LJCaughtException applicationDocumentsDirectory]
00000001000082e8 t +[LJCaughtException getExceptionFileNameWithPrefix:]
00000001000070c0 t +[LJCaughtException getHandler]
00000001000070c4 t +[LJCaughtException processException:]
000000010000708c t +[LJCaughtException setDefaultHandler]
0000000100009414 t -[AppDelegate .cxx_destruct]
0000000100009258 t -[AppDelegate application:didFinishLaunchingWithOptions:]
00000001000093e8 t -[AppDelegate applicationDidBecomeActive:]
00000001000093e0 t -[AppDelegate applicationDidEnterBackground:]
00000001000093e4 t -[AppDelegate applicationWillEnterForeground:]
00000001000093dc t -[AppDelegate applicationWillResignActive:]
00000001000093ec t -[AppDelegate applicationWillTerminate:]
000000010000882c t -[AppDelegate crashHandler]
0000000100009400 t -[AppDelegate setWindow:]
00000001000093f0 t -[AppDelegate window]
0000000100006fc4 t -[LJCrashBackTraceModel .cxx_destruct]
0000000100006d4c t -[LJCrashBackTraceModel encodeWithCoder:]
0000000100006e4c t -[LJCrashBackTraceModel initWithCoder:]
0000000100006f9c t -[LJCrashBackTraceModel setStrImageLoadAddress:]
0000000100006fb8 t -[LJCrashBackTraceModel setStrImageName:]
0000000100006f80 t -[LJCrashBackTraceModel setStrStackAddress:]
0000000100006f8c t -[LJCrashBackTraceModel strImageLoadAddress]
0000000100006fa8 t -[LJCrashBackTraceModel strImageName]
0000000100006f70 t -[LJCrashBackTraceModel strStackAddress]
0000000100006cd0 t -[LJCrashInfoModel .cxx_destruct]
0000000100006cac t -[LJCrashInfoModel aryCrashBackTrace]
0000000100006928 t -[LJCrashInfoModel encodeWithCoder:]
0000000100006aa0 t -[LJCrashInfoModel initWithCoder:]
0000000100006cbc t -[LJCrashInfoModel setAryCrashBackTrace:]
0000000100006c84 t -[LJCrashInfoModel setStrCrashArch:]
0000000100006c68 t -[LJCrashInfoModel setStrCrashName:]
0000000100006c4c t -[LJCrashInfoModel setStrCrashReason:]
0000000100006ca0 t -[LJCrashInfoModel setStrCrashSystemVersion:]
0000000100006c74 t -[LJCrashInfoModel strCrashArch]
0000000100006c58 t -[LJCrashInfoModel strCrashName]
0000000100006c3c t -[LJCrashInfoModel strCrashReason]
0000000100006c90 t -[LJCrashInfoModel strCrashSystemVersion]
0000000100008744 t -[ViewController didReceiveMemoryWarning]
0000000100008404 t -[ViewController exceptionAction:]
0000000100008410 t -[ViewController function1]
000000010000846c t -[ViewController function2]
00000001000084c8 t -[ViewController function3]
0000000100008524 t -[ViewController function4]
0000000100008580 t -[ViewController function5]
00000001000085dc t -[ViewController function6]
0000000100008638 t -[ViewController function7]
0000000100008694 t -[ViewController function8]
00000001000086f0 t -[ViewController function9]
000000010000873c t -[ViewController signalCrashAction:]
00000001000083d0 t -[ViewController viewDidLoad]
0000000100008778 t -[ViewController1 viewDidLoad]
000000010000e2b0 s _OBJC_CLASS_$_AppDelegate
000000010000e198 s _OBJC_CLASS_$_LJCaughtException
000000010000e148 s _OBJC_CLASS_$_LJCrashBackTraceModel
000000010000e0f8 s _OBJC_CLASS_$_LJCrashInfoModel
000000010000e1e8 s _OBJC_CLASS_$_ViewController
000000010000e238 s _OBJC_CLASS_$_ViewController1
000000010000e0f0 s _OBJC_IVAR_$_AppDelegate._window
000000010000e0e8 s _OBJC_IVAR_$_LJCrashBackTraceModel._strImageLoadAddress
000000010000e0ec s _OBJC_IVAR_$_LJCrashBackTraceModel._strImageName
000000010000e0e4 s _OBJC_IVAR_$_LJCrashBackTraceModel._strStackAddress
000000010000e0e0 s _OBJC_IVAR_$_LJCrashInfoModel._aryCrashBackTrace
000000010000e0d8 s _OBJC_IVAR_$_LJCrashInfoModel._strCrashArch
000000010000e0d4 s _OBJC_IVAR_$_LJCrashInfoModel._strCrashName
000000010000e0d0 s _OBJC_IVAR_$_LJCrashInfoModel._strCrashReason
000000010000e0dc s _OBJC_IVAR_$_LJCrashInfoModel._strCrashSystemVersion
000000010000e288 s _OBJC_METACLASS_$_AppDelegate
000000010000e1c0 s _OBJC_METACLASS_$_LJCaughtException
000000010000e170 s _OBJC_METACLASS_$_LJCrashBackTraceModel
000000010000e120 s _OBJC_METACLASS_$_LJCrashInfoModel
000000010000e210 s _OBJC_METACLASS_$_ViewController
000000010000e260 s _OBJC_METACLASS_$_ViewController1
0000000100007018 t _UncaughtExceptionHandler
0000000100000000 T __mh_execute_header
0000000100009428 t _getAppName
0000000100009ac4 t _getCodeArch
0000000100009908 t _getImageInfo
00000001000094fc t _getImageLoadAddress
00000001000095dc t _getProgressBinaryImagesInfo
000000010000e410 s _globalProgressInfo
0000000100008b6c t _handleExceptions
00000001000087ac t _main
000000010000e3f8 s _preHander
000000010000e400 s _realPath
000000010000e408 s _signCrashPath
00000001000088c0 t _signalHandler
000000010000916c t _signalMessageBy
符号文件一个记录文件,记录当前工程中的文件编译之后在不同架构上的偏移量。进程在加载的时候都会从一个随机地址开始加载,mach-o中的section也是相对于这个初始的加载地址开始累加的,所以在奔溃的时候,可以通过奔溃堆栈的地址 - 进程加载的地址匹配到出错的函数名称
函数在dSYM中的便宜地址的映射 = 堆栈奔溃地址 - 进程记载地址 + slide(一般————Text代码段的slide是0x00000001000000000)
也可以通过如下方法查看dSYM文件中的slide
otool -arch <arch> -l <path_to_dsym> | grep __TEXT -m 2 -A 1 | grep vmaddr
拿当前我们奔溃的堆栈来说
atos -arch arm64 -o TestCrash.app.dSYM/Contents/Resources/DWARF/TestCrash -l 0x100598000 0x00000001005a072c
// 得到的结果为:
-[ViewController function9] (in TestCrash) (ViewController.m:89)
堆栈地址 - 镜像加载的地址 + 0x0000000100000000 = 0x000000010000872c
当前奔溃的函数是function9,对应的堆栈地址是,和上面算出来的地址有出入
00000001000086f0
原因是function9对饮的地址只是function9函数开始的地址,并非是函数真正报错的地址
- (void)function9 {
NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];
}
所以通过nm的方式没能找到对应的报错的函数,换一个命令:
dwarfdump -e --debug-line TestCrash.app.dSYM/Contents/Resources/DWARF/TestCrash > line.txt
出来的结果如下:
----------------------------------------------------------------------
File: TestCrash.app.dSYM/Contents/Resources/DWARF/TestCrash (arm64)
----------------------------------------------------------------------
.debug_line contents:
----------------------------------------------------------------------
debug_line[0x00000000]
----------------------------------------------------------------------
Address Line File
------------------ ------ ------------------------------
0x0000000000000000 1 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include/__stddef_max_align_t.h
----------------------------------------------------------------------
debug_line[0x000000a5]
----------------------------------------------------------------------
Address Line File
------------------ ------ ------------------------------
0x0000000000000000 1 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/fenv.h
......
Address Line File
------------------ ------ ------------------------------
0x00000001000083d0 22 /****/TestCrash/ViewController.m
0x00000001000083dc 23
0x00000001000083f8 25
0x0000000100008404 26
0x0000000100008404 27
0x0000000100008410 30
0x0000000100008434 32
0x0000000100008440 31
0x0000000100008444 31
0x000000010000844c 34
0x000000010000846c 37
0x0000000100008490 39
0x000000010000849c 38
0x00000001000084a0 38
0x00000001000084a8 41
0x00000001000084c8 44
0x00000001000084ec 46
0x00000001000084f8 45
0x00000001000084fc 45
0x0000000100008504 48
0x0000000100008524 51
0x0000000100008548 53
0x0000000100008554 52
0x0000000100008558 52
0x0000000100008560 55
0x0000000100008580 58
0x00000001000085a4 60
0x00000001000085b0 59
0x00000001000085b4 59
0x00000001000085bc 62
0x00000001000085dc 65
0x0000000100008600 67
0x000000010000860c 66
0x0000000100008610 66
0x0000000100008618 69
0x0000000100008638 72
0x000000010000865c 74
0x0000000100008668 73
0x000000010000866c 73
0x0000000100008674 76
0x0000000100008694 79
0x00000001000086b8 81
0x00000001000086c4 80
0x00000001000086c8 80
0x00000001000086d0 83
0x00000001000086f0 86
0x00000001000086fc 87
0x000000010000871c 88
0x000000010000872c 89
0x000000010000873c 91
0x000000010000873c 94
0x0000000100008744 98
0x0000000100008750 99
0x000000010000876c 101
0x0000000100008778 112
0x0000000100008784 113
0x00000001000087a0 115
0x00000001000087ac 115
......
这里对应的地址就能找到了,对应的行数也找出来了。